import axios, { AxiosResponse } from "axios";//导入axios用于发起http请求
// prettier-ignore
import { ElLoading, ElMessage, ElMessageBox, ElNotification } from "element-plus";//导入element-plus库中的组件
import useUserStore from "@/store/modules/user";//导入pinia的用户登录等方法
import { getToken } from "@/utils/auth";//导入获取token的方法
import { blobValidate, tansParams } from "@/utils/lychee";//导入参数处理等工具方法
import errorCode from "@/utils/errorCode";//导入错误码对象
import cache from "@/plugins/cache";//导入cache对象,用于在前端应用中存储和获取数据
import { saveAs } from "file-saver";//用来保存从服务器接收到的二进制数据为文件，比如下载Excel报表或任何其他类型的文件
import { nanoid } from "nanoid";//导入用于生成唯一ID的小型库

const userAgent = navigator.userAgent;//此方法返回一个包含了关于用户浏览器信息的字符串。这个字符串通常包括浏览器名称、版本、操作系统等信息

// 浏览器检测是否是IE11
var isIE11 = userAgent.indexOf("Trident") > -1 && userAgent.indexOf("rv:11.0") > -1;

// 浏览器检测是否小于IE11
var isLessIE11 = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1;

// 设置默认的请求头信息
axios.defaults.headers["Content-Type"] = "application/json;charset=utf-8";

/**获得请求头的Token**/
export const globalHeaders = () => {
	return {
		Authorization: "Bearer " + getToken(),
	};
};

/**创建axios实例**/
export const service = axios.create({
	baseURL: import.meta.env.VITE_APP_BASE_API,//设置请求的baseURL
	timeout: 20000,//设置请求超时时间(当前超时时间是20秒)
});
// 是否显示重新登录
export let isRelogin = { show: false };

/**request请求拦截器**/
service.interceptors.request.use(
	(config: any) => {//config参数对象包含了即将发送的HTTP请求的所有信息（如URL、方法、头部信息、数据等）
		// 检查请求头中是否明确指定了不需要添加Token (isToken: false)。如果未指定，则默认需要添加Token
		const isToken = (config.headers || {}).isToken === false;
		// 检查请求头中是否明确指定了不需要防止重复提交 (repeatSubmit: false)。如果未指定，则默认需要防止重复提交
		const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
		// 使用nanoid生成一个唯一的请求ID，并将其添加到请求头中
		const requestId = nanoid();
		//如果存在有效的Token并且当前请求需要Token，则将Token也添加到请求头中
		if (getToken() && !isToken) {
			// 请求加上随机ID
			config.headers["RequestId"] = requestId;
			// 让请求携带自定义token
			config.headers["Authorization"] = "Bearer " + getToken();
		}
		// get请求映射params参数,对于GET请求，如果存在查询参数，则将这些参数转换为字符串并拼接到URL后面。这里使用了tansParams函数来处理参数转换
		if (config.method === "get" && config.params) {
			let url = config.url + "?" + tansParams(config.params);
			url = url.slice(0, -1);//从字符串的开始位置到倒数第二个字符为止截取字符串,并删除最后一个字符
			config.params = {};//将config.params设置为空对象,这意味着原来的查询参数已经被编码到了URL中，因此不再需要保留在params属性中
			config.url = url;//将修改后的URL赋值给config.url
		}
		// 防止重复提交
		if (!isRepeatSubmit && (config.method === "post" || config.method === "put")) {
			// 创建一个包含请求URL、数据和时间戳的对象requestObj
			const requestObj = {
				url: config.url,
				data: typeof config.data === "object" ? JSON.stringify(config.data) : config.data,
				time: new Date().getTime(),
			};
			// 从缓存中获取最近一次的请求对象sessionObj
			const sessionObj = cache.session.getJSON("sessionObj");
			// 如果sessionObj为空，则将当前请求对象存储到缓存中
			if (sessionObj === undefined || sessionObj === null || sessionObj === "") {
				cache.session.setJSON("sessionObj", requestObj);
			} else {
				const s_url = sessionObj.url; // 请求地址
				const s_data = sessionObj.data; // 请求数据
				const s_time = sessionObj.time; // 请求时间
				const interval = 1000; // 间隔时间(ms)，小于此时间视为重复提交
				// 如果发现当前请求与上一次请求的URL和数据相同，并且两次请求之间的时间差小于设定的间隔（这里是1秒），则认为这是重复提交，给出警告并拒绝此次请求
				if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
					const message = "数据正在处理，请勿重复提交";
					console.warn(`[${s_url}]: ` + message);
					ElMessage({
						message: message,
						type: "error",
						duration: 2 * 1000,
					});
					return Promise.reject(new Error(message));
				} else {// 否则，更新缓存中的sessionObj为当前请求的信息
					cache.session.setJSON("sessionObj", requestObj);
				}
			}
		}
		//最后返回修改后的config对象以继续请求流程
		return config;
	},
	(error) => {//如果发生了任何异常，通过Promise.reject(error)来终止请求并传递错误信息
		console.log(error);
		Promise.reject(error);
	},
);

/**response响应拦截器**/
service.interceptors.response.use((res: AxiosResponse) => {
		// 未设置状态码则默认成功状态
		const code = res.data.code || 200;
		// 获取错误信息,msg变量用于获取对应的错误信息，优先从errorCode映射表中查找，其次是从响应数据中的msg字段获取，最后使用默认错误信息
		const msg = errorCode[code] || res.data.msg || errorCode["default"];
		// 如果响应类型是blob或arraybuffer（通常用于文件下载），直接返回原始数据，不进行进一步处理
		if (res.request.responseType === "blob" || res.request.responseType === "arraybuffer") {
			return res.data;
		}
		if (code === 401) {//当检测到401状态码时，弹出一个确认对话框询问用户是否要重新登录
			if (!isRelogin.show) {
				isRelogin.show = true;
				// prettier-ignore
				ElMessageBox.confirm(
					"登录状态已过期，您可以继续留在该页面，或者重新登录",
					"系统提示",
					{
						confirmButtonText: "重新登录",
						cancelButtonText: "取消",
						type: "warning",
					},
				)
					.then(() => {//选择重新登录，则注销当前用户并跳转至登录页；如果取消，则保持当前状态
						isRelogin.show = false;
						useUserStore().logOut().then(() => {
							location.href = "/index";
						});
					})
					.catch(() => {
						isRelogin.show = false;
					});
			}
			// prettier-ignore
			return Promise.reject("无效的会话，或者会话已过期，请重新登录。");
		} else if (code === 500) {//当检测到500状态码时，弹出一个错误消息提示，并返回一个拒绝的Promise对象
			ElMessage({
				message: msg,
				type: "error",
			});
			return Promise.reject(new Error(msg));
		} else if (code !== 200) {//当检测到非200状态码时，弹出一个错误消息提示，并返回一个拒绝的Promise对象
			ElNotification.error({
				title: msg,
			});
			return Promise.reject("error");
		} else {//如果状态码为200，则直接返回响应数据供后续处理
			return res.data;
		}
	},
	error => {//如果发生了任何异常，通过Promise.reject(error)来终止请求并传递错误信息
		console.log("err", error);
		let { message } = error;
		if (message == "Network Error") {
			message = "后端接口连接异常";
		} else if (message.includes("timeout")) {
			message = "系统接口请求超时";
		} else if (message.includes("Request failed with status code")) {
			message = "系统接口" + message.substr(message.length - 3) + "异常";
		}
		ElMessage({
			message: message,
			type: "error",
			duration: 5 * 1000,
		});
		return Promise.reject(error);
	},
);

export const download = async (url: string, params: any, filename: string) => {
	// prettier-ignore
	const downloadLoadingInstance = ElLoading.service({
		text: "正在下载数据，请稍候",
		spinner: "loading",
		background: "rgba(0, 0, 0, 0.7)",
	});
	let req = null;
	if (params) {
		req = service.post(url, params, {
			transformRequest: [
				(params) => {
					return tansParams(params);
				},
			],
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
			responseType: "blob",
		});
	} else {
		req = service.get(url, {
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
			responseType: "blob",
		});
	}
	return await req
		.then(async (resp: any) => {
			const isLogin = await blobValidate(resp);
			if (isLogin) {
				const blob = new Blob([resp]);
				// saveAs(blob, filename, {
				// 	type: "application/octet-stream;charset=utf-8"
				// });
				saveAs(blob, filename);
				//var blob2 = new Blob([resp], {type: "text/plain;charset=utf-8"});
				//saveAs(blob, resp.msg + ".xlsx");
				console.log("%s ====>>>导出成功", filename);
			} else {
				const resText = resp.text();
				const rspObj = JSON.parse(resText);
				// prettier-ignore
				const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode["default"];
				ElMessage.error(errMsg);
			}
			downloadLoadingInstance.close();
		})
		.catch((r) => {
			console.error(r);
			ElMessage.error("下载文件出现错误，请联系管理员！");
			downloadLoadingInstance.close();
		});
};

export default service;
